package raft

import (
	"database/sql"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strings"
	"time"

	// 使用无 cgo 的 SQLite 驱动，避免 CGO_ENABLED=0 报错
	_ "modernc.org/sqlite"
)

// Applyer 负责将已提交的日志应用到数据库
type Applyer struct {
	db      *sql.DB
	logger  *log.Logger
	nodeID  int
	pieceID int
}

// NewApplyer 创建一个新的数据库应用器（默认启用 schema 初始化）
// 提交用
func NewApplyer(dbPath string, nodeID int, pieceID int) (*Applyer, error) {
	return NewApplyerWithSchema(dbPath, nodeID, pieceID, true)
}

// NewApplyerWithSchema 创建一个新的数据库应用器，可选择是否自动初始化 schema
// dbPath: 数据库文件路径
// nodeID: 节点ID
// pieceID: 分片ID
// initSchema: 是否自动初始化数据库表结构
// - true: 调用 ensureSchemaInitialized 从 SQL 文件初始化（生产环境）
// - false: 跳过初始化，由调用者手动创建表（测试环境）
func NewApplyerWithSchema(dbPath string, nodeID int, pieceID int, initSchema bool) (*Applyer, error) {
	db, err := sql.Open("sqlite", dbPath)
	if err != nil {
		return nil, fmt.Errorf("打开数据库失败: %w", err)
	}

	// 测试连接
	if err := db.Ping(); err != nil {
		db.Close()
		return nil, fmt.Errorf("连接数据库失败: %w", err)
	}

	// 开启外键约束
	if _, err := db.Exec("PRAGMA foreign_keys = ON"); err != nil {
		db.Close()
		return nil, fmt.Errorf("启用外键约束失败: %w", err)
	}

	// 根据参数决定是否初始化 schema
	if initSchema {
		if err := ensureSchemaInitialized(db, dbPath); err != nil {
			db.Close()
			return nil, err
		}
	}

	applyer := &Applyer{
		db:      db,
		logger:  log.New(log.Writer(), fmt.Sprintf("[Applyer%d-%d] ", nodeID, pieceID), log.Lmicroseconds),
		nodeID:  nodeID,
		pieceID: pieceID,
	}

	applyer.logger.Printf("数据库应用器初始化成功，连接到: %s (initSchema=%v)", dbPath, initSchema)
	return applyer, nil
}

// Close 关闭数据库连接
func (a *Applyer) Close() error {
	return a.db.Close()
}

// Apply 处理 ApplyMsg 并执行相应的数据库操作
func (a *Applyer) Apply(msg ApplyMsg) error {
	// ApplyMsg.Command 已经是 LogValue 类型
	logValue := msg.Command

	a.logger.Printf("应用日志[%d]: 操作=%s, 表=%s", msg.Index, logValue.Operation, logValue.TableName)

	switch strings.ToLower(logValue.Operation) {
	case "insert":
		return a.handleInsert(logValue)
	case "update":
		return a.handleUpdate(logValue)
	case "delete":
		return a.handleDelete(logValue)
	case "select":
		return a.handleSelect(logValue)
	default:
		return fmt.Errorf("不支持的操作类型: %s", logValue.Operation)
	}
}

// handleInsert 处理插入操作
func (a *Applyer) handleInsert(lv LogValue) error {
	if lv.Data == nil {
		return fmt.Errorf("insert 操作缺少 data 字段")
	}

	dataMap, ok := lv.Data.(map[string]interface{})
	if !ok {
		return fmt.Errorf("data 字段类型错误，期望 map[string]interface{}，实际 %T", lv.Data)
	}

	if len(dataMap) == 0 {
		return fmt.Errorf("data 字段为空")
	}

	// 构建 INSERT 语句
	var columns []string
	var placeholders []string
	var values []interface{}

	for col, val := range dataMap {
		columns = append(columns, col)
		placeholders = append(placeholders, "?")
		values = append(values, a.convertValue(val))
	}

	query := fmt.Sprintf(
		"INSERT INTO %s (%s) VALUES (%s)",
		lv.TableName,
		strings.Join(columns, ", "),
		strings.Join(placeholders, ", "),
	)

	a.logger.Printf("执行 INSERT: %s, 参数: %v", query, values)

	result, err := a.db.Exec(query, values...)
	if err != nil {
		// 增强调试日志：打印错误、参数类型，以及表结构，便于定位 datatype mismatch 等问题
		a.logger.Printf("INSERT 失败: %v", err)
		// 打印本次插入对应的 LogValue 内容
		a.logger.Printf("LogValue: operation=%s table=%s data=%v condition=%v", lv.Operation, lv.TableName, lv.Data, lv.Condition)
		for i, v := range values {
			a.logger.Printf("参数[%d]=%v (GoType=%T)", i, v, v)
		}
		// 输出表结构信息
		a.logTableInfo(lv.TableName)
		return fmt.Errorf("INSERT 失败: %w", err)
	}

	rowsAffected, _ := result.RowsAffected()
	a.logger.Printf("INSERT 成功，影响行数: %d", rowsAffected)
	return nil
}

// handleUpdate 处理更新操作
func (a *Applyer) handleUpdate(lv LogValue) error {
	if lv.Data == nil {
		return fmt.Errorf("update 操作缺少 data 字段")
	}

	dataMap, ok := lv.Data.(map[string]interface{})
	if !ok {
		return fmt.Errorf("data 字段类型错误，期望 map[string]interface{}，实际 %T", lv.Data)
	}

	if len(dataMap) == 0 {
		return fmt.Errorf("data 字段为空")
	}

	// 构建 UPDATE 语句
	var setClauses []string
	var values []interface{}

	for col, val := range dataMap {
		setClauses = append(setClauses, fmt.Sprintf("%s = ?", col))
		values = append(values, a.convertValue(val))
	}

	// 构建 WHERE 子句
	whereClause, whereValues := a.buildWhereClause(lv.Condition)
	values = append(values, whereValues...)

	query := fmt.Sprintf(
		"UPDATE %s SET %s%s",
		lv.TableName,
		strings.Join(setClauses, ", "),
		whereClause,
	)

	a.logger.Printf("执行 UPDATE: %s, 参数: %v", query, values)

	result, err := a.db.Exec(query, values...)
	if err != nil {
		return fmt.Errorf("UPDATE 失败: %w", err)
	}

	rowsAffected, _ := result.RowsAffected()
	a.logger.Printf("UPDATE 成功，影响行数: %d", rowsAffected)
	return nil
}

// handleDelete 处理删除操作
func (a *Applyer) handleDelete(lv LogValue) error {
	// 构建 WHERE 子句
	whereClause, whereValues := a.buildWhereClause(lv.Condition)

	query := fmt.Sprintf("DELETE FROM %s%s", lv.TableName, whereClause)

	a.logger.Printf("执行 DELETE: %s, 参数: %v", query, whereValues)

	result, err := a.db.Exec(query, whereValues...)
	if err != nil {
		return fmt.Errorf("DELETE 失败: %w", err)
	}

	rowsAffected, _ := result.RowsAffected()
	a.logger.Printf("DELETE 成功，影响行数: %d", rowsAffected)
	return nil
}

// handleSelect 处理查询操作（只记录日志，不实际返回结果）
// 注意：Raft 日志应用通常用于写操作，查询操作可以直接读取数据库
func (a *Applyer) handleSelect(lv LogValue) error {
	a.logger.Printf("收到 SELECT 操作请求，表: %s, 条件: %v", lv.TableName, lv.Condition)
	// SELECT 通常不需要通过 Raft 共识，可以直接读取本地数据库
	// 这里仅记录日志，实际查询应由上层直接调用数据库
	return nil
}

// buildWhereClause 根据条件构建 WHERE 子句
func (a *Applyer) buildWhereClause(condition map[string]interface{}) (string, []interface{}) {
	if len(condition) == 0 {
		return "", nil
	}

	var whereClauses []string
	var values []interface{}

	for col, val := range condition {
		whereClauses = append(whereClauses, fmt.Sprintf("%s = ?", col))
		values = append(values, a.convertValue(val))
	}

	whereClause := " WHERE " + strings.Join(whereClauses, " AND ")
	return whereClause, values
}

// convertValue 将 interface{} 值转换为数据库可接受的类型
func (a *Applyer) convertValue(val interface{}) interface{} {
	switch v := val.(type) {
	case string:
		// 尝试解析时间字符串
		if t, err := time.Parse(time.RFC3339, v); err == nil {
			return t.Format("2006-01-02 15:04:05")
		}
		return v
	case float64:
		// JSON 数字通常被解析为 float64，需要判断是否应该转为 int
		if v == float64(int64(v)) {
			return int64(v)
		}
		return v
	case int, int32, int64, uint, uint32, uint64:
		return v
	case bool:
		if v {
			return 1
		}
		return 0
	case nil:
		return nil
	default:
		// 其他类型转为字符串
		return fmt.Sprintf("%v", v)
	}
}

// logTableInfo 打印指定表的结构信息，辅助排查类型不匹配等问题
func (a *Applyer) logTableInfo(table string) {
	if table == "" {
		a.logger.Printf("无法打印表结构：表名为空")
		return
	}
	// 注意：此处仅用于调试日志输出
	query := fmt.Sprintf("PRAGMA table_info(%s)", table)
	rows, err := a.db.Query(query)
	if err != nil {
		a.logger.Printf("获取表结构失败: %v", err)
		return
	}
	defer rows.Close()

	a.logger.Printf("表 %s 结构:", table)
	for rows.Next() {
		var (
			cid     int
			name    string
			ctype   string
			notnull int
			dflt    sql.NullString
			pk      int
		)
		if err := rows.Scan(&cid, &name, &ctype, &notnull, &dflt, &pk); err != nil {
			a.logger.Printf("读取表结构行失败: %v", err)
			continue
		}
		a.logger.Printf(" - %d: %s %s NOTNULL=%d PK=%d DEFAULT=%v", cid, name, ctype, notnull, pk, dflt)
	}
	if err := rows.Err(); err != nil {
		a.logger.Printf("遍历表结构失败: %v", err)
	}
}

// BatchApply 批量应用多个 ApplyMsg
func (a *Applyer) BatchApply(messages []ApplyMsg) error {
	tx, err := a.db.Begin()
	if err != nil {
		return fmt.Errorf("开启事务失败: %w", err)
	}

	for _, msg := range messages {
		if err := a.Apply(msg); err != nil {
			tx.Rollback()
			return fmt.Errorf("批量应用失败，索引 %d: %w", msg.Index, err)
		}
	}

	if err := tx.Commit(); err != nil {
		return fmt.Errorf("提交事务失败: %w", err)
	}

	a.logger.Printf("批量应用 %d 条日志成功", len(messages))
	return nil
}

// GetDB 返回底层数据库连接（用于直接查询等场景）
func (a *Applyer) GetDB() *sql.DB {
	return a.db
}

// ensureSchemaInitialized 检查关键表是否存在，不存在则执行项目下 sql/sqlite_init.sql 初始化数据库
func ensureSchemaInitialized(db *sql.DB, dbPath string) error {
	var cnt int
	if err := db.QueryRow("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='user'").Scan(&cnt); err != nil {
		return fmt.Errorf("检查表是否存在失败: %w", err)
	}
	if cnt > 0 {
		return nil
	}

	schemaPath := filepath.Join(filepath.Dir(dbPath), "sql", "sqlite_init.sql")
	content, err := os.ReadFile(schemaPath)
	if err != nil {
		return fmt.Errorf("读取 schema 文件失败(%s): %w", schemaPath, err)
	}

	if _, err := db.Exec(string(content)); err != nil {
		return fmt.Errorf("执行 schema 初始化失败: %w", err)
	}
	return nil
}
